package item

import (
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"strings"
	"week13/errors"
	"week13/internal/data"
)

const TABLENAME = "item"
type ItemDo struct {
	ItemId int `json:"itemId" xorm:"pk autoincr INT id <-"`
	ItemName string `json:"itemName" xorm:"varchar(45) name"`
	OnSale int `json:"OnSale" xorm:"INT onsale"`
	Type int `json:"type" xorm:"INT type"`
	Inventory int `json:"inventory" xorm:"INT inventory"`
	Addtime int `json:"addtime" xorm:"INT created addtime"`
	Mtime string `json:"mtime" xorm:"TIMESTAMP mtime updated"`
}
func(item ItemDo) TableName() string{
	return TABLENAME
}
type ItemRepository interface {
	Find(itemId int,cache bool) (ItemDo,error)
	Save(item ItemDo) (int,error)
	DecrInventory(itemId int,num int) (int,error)
	IncrInventory(itemId int, num int) (int,error)
	List(do ItemDo,page int,pageSize int) ([]ItemDo,error)
	FindOneByFilter(Do ItemDo) (ItemDo,error)
	Count(Do ItemDo) (int64,error)
	GetItemFromCache (itemId int) (ItemDo,error)
	SetItemToCache(do ItemDo) error
}

type ItemDao struct {
}
//从缓存中获取
func (dao *ItemDao) GetItemFromCache (itemId int) (ItemDo,error){
	client, err := data.RedisClient()
	defer func() {client.Close()}()

	if err != nil {
		return ItemDo{},err
	}
	key := fmt.Sprintf("fx.itemInfo.%d",itemId)
	data,err := client.Do("get",key)

	jsonData, err := redis.Bytes(data,err)
	if err != nil {
		return ItemDo{}, errors.NewExecuteFail("redis get item error:"+err.Error())
	}
	if len(jsonData) < 1{
		return ItemDo{}, errors.NewNoFound()
	}
	result := ItemDo{}
	err = json.Unmarshal(jsonData,&result)
	if err != nil {
		return ItemDo{}, errors.NewIllegalParam("jsonData can't transfer IncomeDo")
	}
	return result,nil
}
//删除缓存
func (dao *ItemDao) deleteItemFromCache(itemId int) error  {
	client, err := data.RedisClient()
	defer client.Close()
	key := fmt.Sprintf("fx.itemInfo.%d",itemId)
	_,err = client.Do("delete",key)
	if err != nil {
		return errors.NewExecuteFail("redis delete item error,itemId:"+string(itemId))
	}
	return nil
}
//设置缓存
func (dao *ItemDao) SetItemToCache(do ItemDo) error{
	jsonData,err := json.Marshal(do)
	if err != nil {
		return errors.NewIllegalParam("jsonData can't transfer IncomeDo")
	}
	client, err := data.RedisClient()
	defer client.Close()
	key := fmt.Sprintf("fx.itemInfo.%d",do.ItemId)
	_,err = client.Do("set",key,jsonData)
	if err != nil {
		return errors.NewExecuteFail("redis set item error")
	}
	return nil

}
func (dao *ItemDao) Count(Do ItemDo)(int64,error){
	db,err := data.DBEngin()
	if err != nil {
		return 0,errors.NewConnectFail()
	}
	where := make([]string,0)
	values := make([]interface{}, 0)
	if Do.ItemId >0 {
		where = append(where,"id=?")
		values = append(values,Do.ItemId)
	}
	if Do.ItemName != "" {
		where  = append(where,"name like '%?%'")
		values = append(values,Do.ItemName)
	}
	if len(where)<1{
		return 0,errors.NewIllegalParam("Find One item  lack where params")
	}
	whereStr := strings.Join(where," AND ")
	do2 := new(ItemDo)
	total, err := db.Where(whereStr, values...).Count(do2)
	if err != nil {
		return 0,errors.NewExecuteFail(err.Error())
	}
	return total,nil
}
func (dao *ItemDao) FindOneByFilter(Do ItemDo) (ItemDo,error)  {

	db,err := data.DBEngin()
	if err != nil {
		return ItemDo{},errors.NewConnectFail()
	}
	where := make([]string,0)
	values := make([]interface{},0)
	if Do.ItemId >0 {
		where = append(where,"id=?")
		values = append(values,Do.ItemId)
	}
	if Do.ItemName != "" {
		where  = append(where,"name like '%?%'")
		values = append(values,Do.ItemName)
	}

	if len(where)<1{
		return ItemDo{},errors.NewIllegalParam("Find One item  lack where params")
	}
	whereStr := strings.Join(where," AND ")
	data := ItemDo{}
	ret,err := db.Where(whereStr,values...).Get(&data)
	if err != nil {
		return data,errors.NewExecuteFail(err.Error())
	}
	if !ret {
		return data, errors.NewNoFound()
	}

	return data,nil
}
//商品列表
func (dao *ItemDao) List(Do ItemDo,page int,pageSize int) ([]ItemDo,error)  {
	db,err := data.DBEngin()
	if err != nil {
		return nil,errors.NewConnectFail()
	}
	where := make([]string,0)
	values := make([]interface{}, 0)
	if Do.ItemId >0 {
		where = append(where,"id=?")
		values = append(values,Do.ItemId)
	}
	if Do.ItemName != "" {
		where  = append(where,"name like '%?%'")
		values = append(values,Do.ItemName)
	}
	if len(where)<1{
		return nil,errors.NewIllegalParam("Find One income  lack where params")
	}
	whereStr := strings.Join(where," AND ")
	incomes := make([]ItemDo,0)
	if page>=1 {
		db.Where(whereStr,values...).Limit(pageSize,(page-1)*pageSize).Find(&incomes)
	} else {
		db.Where(whereStr,values...).Find(&incomes)
	}
	if err != nil {
		return nil, errors.NewExecuteFail("item list error")
	}
	return incomes,nil
}
//查找商品
func (dao *ItemDao) Find(itemId int,cache bool) (ItemDo,error)  {
	var err error
	var itemDo ItemDo
	if cache == true {
		itemDo, err = dao.GetItemFromCache(itemId)
		if err == nil {
			return itemDo,nil
		}
	}
	db,err := data.DBEngin()
	if err != nil {
		return ItemDo{},errors.NewConnectFail()
	}
	itemDo = ItemDo{}
	ret,err := db.ID(itemId).Get(&itemDo)
	if !ret {
		return itemDo, errors.NewNoFound()
	}
	if err != nil {
		return itemDo,errors.NewExecuteFail(err.Error())
	}
	return itemDo,nil
}
//保存商品数据
func (dao *ItemDao) Save(do ItemDo) (int, error)   {
	db,err := data.DBEngin()
	if err != nil {
		return 0,errors.NewConnectFail()
	}
	var affected int64=0
	if do.ItemId == 0 {
		affected,err = db.Insert(&do)
		if err !=  nil {
			return 0, errors.NewExecuteFail(err.Error())
		}
		if affected ==0 {
			return 0,errors.NewExecuteFail("INSERT ERROR")
		}
		return do.ItemId,nil
	}
	ItemId := do.ItemId
	do.ItemId = 0
	dao.deleteItemFromCache(ItemId)
	affected, err = db.ID(ItemId).Update(do)
	if err !=  nil {
		return 0, errors.NewExecuteFail(err.Error())
	}
	if affected == 0 {
		return 0, errors.NewNothingChange()
	}
	return ItemId,nil
}
//减少库存
func (dao *ItemDao) DecrInventory(itemId int,num int) (int,error){
	db,err := data.DBEngin()
	if err != nil {
		return 0,errors.NewConnectFail()
	}
	dao.deleteItemFromCache(itemId)
	sql := "update "+TABLENAME+" set inventory = inventory-? where id = ? and inventory>=?"
	res, err := db.Exec(sql, num, itemId,num)
	if err != nil {
		return 0, errors.NewExecuteFail(err.Error())
	}
	affected, err := res.RowsAffected()
	if err != nil {
		return 0, errors.NewExecuteFail(err.Error())
	}
	return int(affected),err
}
//增加库存
func (dao *ItemDao) IncrInventory(itemId int, num int) (int,error){
	db,err := data.DBEngin()
	if err != nil {
		return 0,errors.NewConnectFail()
	}
	dao.deleteItemFromCache(itemId)
	sql := "update "+TABLENAME+" set inventory = inventory+? where id = ?"
	res, err := db.Exec(sql, num, itemId)
	if err != nil {
		return 0, errors.NewExecuteFail(err.Error())
	}
	affected, err := res.RowsAffected()
	if err != nil {
		return 0, errors.NewExecuteFail(err.Error())
	}
	return int(affected),err
}